////////////////////////////////////////////////////////////////////////////
//
//  Crytek Engine Source File.
//  Copyright (C), Crytek Studios, 2001.
// -------------------------------------------------------------------------
//  File name:   AreaBox.cpp
//  Version:     v1.00
//  Created:     22/10/2002 by Lennert.
//  Compilers:   Visual C++ 6.0
//  Description: CAreaBox implementation.
// -------------------------------------------------------------------------
//  History:
//
////////////////////////////////////////////////////////////////////////////

#include "StdAfx.h"
#include "AreaBox.h"
#include "Viewport.h"
#include "PropertiesPanel.h"
#include "SegmentedWorld/SegmentedWorldManager.h"

#include <IEntity.h>

//////////////////////////////////////////////////////////////////////////
void CAreaObjectBase::PostClone( CBaseObject *pFromObject,CObjectCloneContext &ctx )
{
	__super::PostClone( pFromObject,ctx );

	CAreaObjectBase *pFrom = (CAreaObjectBase*)pFromObject;
	// Clone event targets.
	if (!pFrom->m_entities.empty())
	{
		int numEntities = pFrom->m_entities.size();
		for (int i = 0; i < numEntities; i++)
		{
			GUID guid = ctx.ResolveClonedID(pFrom->m_entities[i]);
			if (guid != GUID_NULL)
			{
				m_entities.push_back(guid);
			}
		}
	}
}

//////////////////////////////////////////////////////////////////////////
void CAreaObjectBase::AddEntity( CBaseObject *pEntity )
{
	assert( pEntity );
	// Check if this entity already binded.
	if (std::find(m_entities.begin(),m_entities.end(),pEntity->GetId()) != m_entities.end())
		return;

	StoreUndo( "Add Entity" );
	m_entities.push_back(pEntity->GetId());
	UpdateGameArea();
	SW_ON_OBJ_MOD(this);
}

//////////////////////////////////////////////////////////////////////////
void CAreaObjectBase::RemoveEntity( int index )
{
	assert( index >= 0 && index < m_entities.size() );
	StoreUndo( "Remove Entity" );

	m_entities.erase( m_entities.begin()+index );
	UpdateGameArea();
	SW_ON_OBJ_MOD(this);
}

//////////////////////////////////////////////////////////////////////////
CEntityObject* CAreaObjectBase::GetEntity( int index )
{
	assert( index >= 0 && index < m_entities.size() );
	CBaseObject *pObject = GetObjectManager()->FindObject(m_entities[index]);
	if (pObject && pObject->IsKindOf(RUNTIME_CLASS(CEntityObject)))
	{
		return (CEntityObject*)pObject;
	}
	return 0;
}

//////////////////////////////////////////////////////////////////////////
// CBase implementation.
//////////////////////////////////////////////////////////////////////////
IMPLEMENT_DYNCREATE(CAreaBox,CEntityObject)

int CAreaBox::m_nRollupId														= 0;
CPickEntitiesPanel* CAreaBox::m_pPanel							= NULL;
int CAreaBox::m_rollupMultyId												= 0;
CPropertiesPanel* CAreaBox::m_pSoundPropertiesPanel = NULL;
int CAreaBox::m_nSoundPanelID												= 0;
CVarBlockPtr CAreaBox::m_pSoundPanelVarBlock				= NULL;

//////////////////////////////////////////////////////////////////////////
CAreaBox::CAreaBox()
{
	m_areaId = -1;
	m_edgeWidth=0;
	mv_width = 4;
	mv_length = 4;
	mv_height = 1;
	mv_groupId = 0;
	mv_priority = 0;
	mv_filled = false;
	mv_displaySoundInfo = false;
	m_entityClass = "AreaBox";

	// Resize for 6 sides
	m_abObstructSound.resize(6);

	m_box.min=Vec3(-mv_width/2, -mv_length/2, 0);
	m_box.max=Vec3(mv_width/2, mv_length/2, mv_height);

	m_bIgnoreGameUpdate = 1;

	if(!m_pSoundPanelVarBlock) // Static
		m_pSoundPanelVarBlock = new CVarBlock;
}

//////////////////////////////////////////////////////////////////////////
void CAreaBox::InitVariables()
{
	AddVariable( m_areaId,"AreaId",functor(*this,&CAreaBox::OnAreaChange) );
	AddVariable( m_edgeWidth,"FadeInZone",functor(*this,&CAreaBox::OnSizeChange) );
	AddVariable( mv_width,"Width",functor(*this,&CAreaBox::OnSizeChange) );
	AddVariable( mv_length,"Length",functor(*this,&CAreaBox::OnSizeChange) );
	AddVariable( mv_height,"Height",functor(*this,&CAreaBox::OnSizeChange) );
	AddVariable( mv_groupId,"GroupId",functor(*this,&CAreaBox::OnAreaChange) );
	AddVariable( mv_priority,"Priority",functor(*this,&CAreaBox::OnAreaChange) );
	AddVariable( mv_filled,"Filled",functor(*this,&CAreaBox::OnAreaChange) );
	AddVariable( mv_displaySoundInfo,"DisplaySoundInfo", functor(*this, &CAreaBox::OnSoundParamsChange));
}

//////////////////////////////////////////////////////////////////////////
void CAreaBox::Done()
{
	CEntityObject::Done();
}

//////////////////////////////////////////////////////////////////////////
bool CAreaBox::Init( IEditor *ie,CBaseObject *prev,const CString &file )
{
	m_bIgnoreGameUpdate = 1;

	SetColor( RGB(0,0,255) );
	bool res = CEntityObject::Init( ie,prev,file );

	if (m_entity)
	{
		m_entity->CreateProxy(ENTITY_PROXY_AREA);
	}

	if (prev)
	{
		m_abObstructSound	= ((CAreaBox*)prev)->m_abObstructSound;
		assert(m_abObstructSound.size() == 6);
	}

	m_bIgnoreGameUpdate = 0;

	return res;
}

//////////////////////////////////////////////////////////////////////////
void CAreaBox::GetLocalBounds( AABB &box )
{
	box = m_box;
}

//////////////////////////////////////////////////////////////////////////
bool CAreaBox::HitTest( HitContext &hc )
{
	Vec3 p;
	/*BBox box;
	box = m_box;
	box.Transform( GetWorldTM() );
	float tr = hc.distanceTollerance/2;
	box.min -= Vec3(tr,tr,tr);
	box.max += Vec3(tr,tr,tr);
	if (box.IsIntersectRay(hc.raySrc,hc.rayDir,p ))
	{
		hc.dist = Vec3(hc.raySrc - p).Length();
		return true;
	}*/
	Matrix34 invertWTM = GetWorldTM();
	Vec3 worldPos = invertWTM.GetTranslation();
	invertWTM.Invert();

	Vec3 xformedRaySrc = invertWTM.TransformPoint(hc.raySrc);
	Vec3 xformedRayDir = invertWTM.TransformVector(hc.rayDir).GetNormalized();

	float epsilonDist = max(.1f, hc.view->GetScreenScaleFactor( worldPos ) * 0.01f);
	epsilonDist *= max(0.0001f, min(invertWTM.GetColumn0().GetLength(), min(invertWTM.GetColumn1().GetLength(), invertWTM.GetColumn2().GetLength()) ));
	float hitDist;

	float tr = hc.distanceTolerance/2 + 1;
	AABB box;
	box.min = m_box.min - Vec3(tr+epsilonDist,tr+epsilonDist,tr+epsilonDist);
	box.max = m_box.max + Vec3(tr+epsilonDist,tr+epsilonDist,tr+epsilonDist);
	
	if (Intersect::Ray_AABB( xformedRaySrc,xformedRayDir,box, p ))
	{
		if (Intersect::Ray_AABBEdge( xformedRaySrc,xformedRayDir,m_box,epsilonDist,hitDist,p ))
		{
			hc.dist = xformedRaySrc.GetDistance(p);
			return true;
		}
	}
	return false;
}

//////////////////////////////////////////////////////////////////////////
void CAreaBox::BeginEditParams( IEditor *ie,int flags )
{
	CEntityObject::BeginEditParams( ie,flags );
	if (!m_pPanel)
	{
		m_pPanel = new CPickEntitiesPanel;
		m_pPanel->Create( CPickEntitiesPanel::IDD,AfxGetMainWnd() );
		m_nRollupId = ie->AddRollUpPage( ROLLUP_OBJECTS,"Attached Entities",m_pPanel );
	}
	if (m_pPanel)
		m_pPanel->SetOwner(this);

	// Make sure to first remove any old sound-obstruction-roll-up-page in case EndEditParams() didn't get called on a previous instance
	if (m_nSoundPanelID != 0)
	{
		// Internally a var block holds "IVariablePtr", on destroy delete is already called
		m_pSoundPanelVarBlock->Clear();

		ie->RemoveRollUpPage(ROLLUP_OBJECTS, m_nSoundPanelID);
		m_nSoundPanelID = 0;
		m_pSoundPropertiesPanel	= new CPropertiesPanel(AfxGetMainWnd());
	}
	else if (!m_pSoundPropertiesPanel) // Static
		m_pSoundPropertiesPanel	= new CPropertiesPanel(AfxGetMainWnd());

	if (m_bIgnoreGameUpdate == 0)
		UpdateSoundPanelParams();
}

//////////////////////////////////////////////////////////////////////////
void CAreaBox::EndEditParams( IEditor *ie )
{
	if (m_nRollupId)
		ie->RemoveRollUpPage( ROLLUP_OBJECTS, m_nRollupId);
	m_nRollupId = 0;
	m_pPanel = NULL;

	if(m_nSoundPanelID != 0)
	{
		// Internally a var block holds "IVariablePtr", on destroy delete is already called
		m_pSoundPanelVarBlock->Clear();

		ie->RemoveRollUpPage(ROLLUP_OBJECTS, m_nSoundPanelID);
		m_nSoundPanelID					= 0;
		m_pSoundPropertiesPanel	= NULL;
	}

	CEntityObject::EndEditParams( ie );
}

//////////////////////////////////////////////////////////////////////////
void CAreaBox::EnableEditParams(bool bEnable)
{
	CEntityObject::EnableEditParams(bEnable);
	if (m_pPanel)
		m_pPanel->SetEnable(bEnable);
}

void CAreaBox::EnableEditMultiSelParams(bool bEnable, bool bAllOfSameType)
{
	CEntityObject::EnableEditMultiSelParams(bEnable, bAllOfSameType);
}

//////////////////////////////////////////////////////////////////////////
void CAreaBox::BeginEditMultiSelParams( bool bAllOfSameType )
{
	__super::BeginEditMultiSelParams( bAllOfSameType );

	if(!bAllOfSameType)
		return;

	// Set to 1 to indicate that we're multi-selecting
	m_rollupMultyId = 1;
}

//////////////////////////////////////////////////////////////////////////
void CAreaBox::EndEditMultiSelParams()
{
	// We're done with multi-selection
	m_rollupMultyId = 0;

	__super::EndEditMultiSelParams();
}

//////////////////////////////////////////////////////////////////////////
void CAreaBox::OnAreaChange(IVariable *pVar)
{
	UpdateGameArea();
}

//////////////////////////////////////////////////////////////////////////
void CAreaBox::OnSizeChange(IVariable *pVar)
{
	Vec3 size( 0,0,0 );
	size.x = mv_width;
	size.y = mv_length;
	size.z = mv_height;

	m_box.min = -size/2;
	m_box.max = size/2;
	// Make volume position bottom of bounding box.
	m_box.min.z = 0;
	m_box.max.z = size.z;
	UpdateGameArea();
}

//////////////////////////////////////////////////////////////////////////
void CAreaBox::Display( DisplayContext &dc )
{
	if(!mv_displaySoundInfo)
	{
		COLORREF wireColor,solidColor;
		float wireOffset = 0;
		float alpha = 0.3f;
		if (IsSelected())
		{
			wireColor = dc.GetSelectedColor();
			solidColor = GetColor();
			wireOffset = -0.1f;
		}
		else
		{
			wireColor = GetColor();
			solidColor = GetColor();
		}

		//dc.renderer->SetCullMode( R_CULL_DISABLE );

		dc.PushMatrix( GetWorldTM() );
		AABB box=m_box;

		bool bFrozen = IsFrozen();

		if (bFrozen)
			dc.SetFreezeColor();
		//////////////////////////////////////////////////////////////////////////
		if (!bFrozen)
			dc.SetColor( solidColor,alpha );

		if (IsSelected())
		{
			dc.DepthWriteOff();
			dc.DrawSolidBox( box.min,box.max );
			dc.DepthWriteOn();
		}

		if (!bFrozen)
			dc.SetColor( wireColor,1 );

		if (IsSelected())
		{
			dc.SetLineWidth(3.0f);
			dc.DrawWireBox( box.min,box.max );
			dc.SetLineWidth(0);
		}
		else
			dc.DrawWireBox( box.min,box.max );
		if (m_edgeWidth)
		{
			float fFadeScale=m_edgeWidth/200.0f;
			AABB InnerBox=box;
			Vec3 EdgeDist=InnerBox.max-InnerBox.min;
			InnerBox.min.x+=EdgeDist.x*fFadeScale; InnerBox.max.x-=EdgeDist.x*fFadeScale;
			InnerBox.min.y+=EdgeDist.y*fFadeScale; InnerBox.max.y-=EdgeDist.y*fFadeScale;
			InnerBox.min.z+=EdgeDist.z*fFadeScale; InnerBox.max.z-=EdgeDist.z*fFadeScale;
			dc.DrawWireBox( InnerBox.min,InnerBox.max );
		}
		if (mv_filled)
		{
			dc.SetAlpha(0.2f);
			dc.DrawSolidBox( box.min,box.max );
		}
		//////////////////////////////////////////////////////////////////////////

		dc.PopMatrix();

		if (!m_entities.empty())
		{
			Vec3 vcol = Vec3(1, 1, 1);
			for (int i = 0; i < m_entities.size(); i++)
			{
				CBaseObject *obj = GetEntity(i);
				if (!obj)
					continue;
				dc.DrawLine(GetWorldPos(), obj->GetWorldPos(), ColorF(vcol.x,vcol.y,vcol.z,0.7f), ColorF(1,1,1,0.7f) );
			}
		}

		DrawDefault(dc);
	}
	else
	{
		ColorB const oObstructionFilled(255, 0, 0, 120);
		ColorB const oObstructionFilledNotSelected(255, 0, 0, 30);
		ColorB const oObstructionNotFilled(255, 0, 0, 10);
		ColorB const oNoObstructionFilled(0, 255, 0, 120);
		ColorB const oNoObstructionFilledNotSelected(0, 255, 0, 30);
		ColorB const oNoObstructionNotFilled(0, 255, 0, 10);

		COLORREF oWireColor;
		if (IsSelected())
			oWireColor = dc.GetSelectedColor();
		else
			oWireColor = GetColor();

		dc.SetColor(oWireColor);

		dc.PushMatrix(GetWorldTM());

		if(mv_height > 0.0f)
			dc.DrawWireBox(m_box.min, m_box.max);
	
		Vec3 const v3BoxMin = m_box.min;
		Vec3 const v3BoxMax = m_box.max;

		// Now build the 6 segments
		float const fLength = v3BoxMax.x - v3BoxMin.x;
		float const fWidth	= v3BoxMax.y - v3BoxMin.y;
		float const fHeight = v3BoxMax.z - v3BoxMin.z;

		Vec3 const v0(v3BoxMin);
		Vec3 const v1(Vec3(v3BoxMin.x+fLength,	v3BoxMin.y,					v3BoxMin.z));
		Vec3 const v2(Vec3(v3BoxMin.x+fLength,	v3BoxMin.y+fWidth,	v3BoxMin.z));
		Vec3 const v3(Vec3(v3BoxMin.x,					v3BoxMin.y+fWidth,	v3BoxMin.z));
		Vec3 const v4(Vec3(v3BoxMin.x,					v3BoxMin.y,					v3BoxMin.z+fHeight));
		Vec3 const v5(Vec3(v3BoxMin.x+fLength,	v3BoxMin.y,					v3BoxMin.z+fHeight));
		Vec3 const v6(Vec3(v3BoxMin.x+fLength,	v3BoxMin.y+fWidth,	v3BoxMin.z+fHeight));
		Vec3 const v7(Vec3(v3BoxMin.x,					v3BoxMin.y+fWidth,	v3BoxMin.z+fHeight));

		// Describe the box faces
		SBoxFace const aoBoxFaces[6] =
		{
			SBoxFace(&v0, &v4, &v5, &v1),
			SBoxFace(&v1, &v5, &v6, &v2),
			SBoxFace(&v2, &v6, &v7, &v3),
			SBoxFace(&v3, &v7, &v4, &v0),
			SBoxFace(&v4, &v5, &v6, &v7),
			SBoxFace(&v0, &v1, &v2, &v3)
		};

		// Draw each side
		unsigned int const nSideCount = ((mv_height>0.0f)?6:4);
		for (unsigned int i = 0; i < nSideCount; ++i)
		{
			if (mv_height == 0.0f)
			{
				if (IsSelected())
				{
					if (m_abObstructSound[i])
						dc.SetColor(oObstructionFilled);
					else
						dc.SetColor(oNoObstructionFilled);
				}
				else
					dc.SetColor(oWireColor);

				dc.DrawLine(*(aoBoxFaces[i].pP1), *(aoBoxFaces[i].pP4));
			}

			if (mv_height > 0.0f)
			{
				// Manipulate the color so it looks a little thicker and redder
				if (mv_filled || gSettings.viewports.bFillSelectedShapes)
				{
					ColorB const nColor = dc.GetColor();

					if (m_abObstructSound[i])
						dc.SetColor(IsSelected()?oObstructionFilled:oObstructionFilledNotSelected);
					else
						dc.SetColor(IsSelected()?oNoObstructionFilled:oNoObstructionFilledNotSelected);

					dc.CullOff();
					dc.DepthWriteOff();

					dc.DrawQuad(*(aoBoxFaces[i].pP1), *(aoBoxFaces[i].pP2), *(aoBoxFaces[i].pP3), *(aoBoxFaces[i].pP4));
					
					dc.DepthWriteOn();
					dc.CullOn();
					dc.SetColor(nColor);
				}
				else if (IsSelected())
				{
					ColorB const nColor = dc.GetColor();

					if (m_abObstructSound[i])
						dc.SetColor(oObstructionNotFilled);
					else
						dc.SetColor(oNoObstructionNotFilled);

					dc.CullOff();
					dc.DepthWriteOff();

					dc.DrawQuad(*(aoBoxFaces[i].pP1), *(aoBoxFaces[i].pP2), *(aoBoxFaces[i].pP3), *(aoBoxFaces[i].pP4));
					
					dc.DepthWriteOn();
					dc.CullOn();
					dc.SetColor(nColor);
				}
			}
		}

		//////////////////////////////////////////////////////////////////////////
		dc.PopMatrix();

		if (!m_entities.empty())
		{
			Vec3 vcol = Vec3(1, 1, 1);
			for (int i = 0; i < m_entities.size(); i++)
			{
				CBaseObject *obj = GetEntity(i);
				if (!obj)
					continue;
				dc.DrawLine(GetWorldPos(), obj->GetWorldPos(), ColorF(vcol.x,vcol.y,vcol.z,0.7f), ColorF(1,1,1,0.7f) );
			}
		}

		DrawDefault(dc);
	}
}

//////////////////////////////////////////////////////////////////////////
void CAreaBox::InvalidateTM( int nWhyFlags )
{
	CEntityObject::InvalidateTM(nWhyFlags);
	UpdateGameArea();
}

//////////////////////////////////////////////////////////////////////////
void CAreaBox::Serialize( CObjectArchive &ar )
{
	m_bIgnoreGameUpdate = 1;
	CEntityObject::Serialize( ar );
	m_bIgnoreGameUpdate = 0;

	XmlNodeRef xmlNode = ar.node;
	if (ar.bLoading)
	{
		m_entities.clear();


		AABB box;
		xmlNode->getAttr( "BoxMin",box.min );
		xmlNode->getAttr( "BoxMax",box.max );

		// Load Entities.
		XmlNodeRef entities = xmlNode->findChild( "Entities" );
		if (entities)
		{
			for (int i = 0; i < entities->getChildCount(); i++)
			{
				XmlNodeRef ent = entities->getChild(i);
				GUID entityId;
				ent->getAttr( "Id",entityId );
				entityId = ar.ResolveID(entityId);
				m_entities.push_back(entityId);
			}
		}
		SetAreaId( m_areaId );
		SetBox(box);
	}
	else
	{
		// Saving.
//		xmlNode->setAttr( "AreaId",m_areaId );
		xmlNode->setAttr( "BoxMin",m_box.min );
		xmlNode->setAttr( "BoxMax",m_box.max );
		// Save Entities.
		if (!m_entities.empty())
		{
			XmlNodeRef nodes = xmlNode->newChild( "Entities" );
			for (int i = 0; i < m_entities.size(); i++)
			{
				XmlNodeRef entNode = nodes->newChild( "Entity" );
				entNode->setAttr( "Id",m_entities[i] );
			}
		}
	}

	// Now serialize the sound variables
	if(m_pSoundPanelVarBlock)
	{
		if(ar.bLoading)
		{
			// Create the variables
			// First clear the remains out
			m_pSoundPanelVarBlock->Clear();

			for(size_t i = 0; i < ((mv_height>0.0f)?6:4); ++i)
			{
				CVariable<bool>* const pvTemp = new CVariable<bool>;

				stack_string cTemp;
				cTemp.Format("Side%d", i+1);

				// And add each to the block
				m_pSoundPanelVarBlock->AddVariable(pvTemp, cTemp);
			}

			// Now read in the data
			XmlNodeRef pSoundDataNode = xmlNode->findChild("SoundData");
			if(pSoundDataNode)
			{
				// Make sure we stay backwards compatible and also find the old "Side:" formatting
				// This can go out once every level got saved with the new formatting
				char const* const pcTemp = pSoundDataNode->getAttr("Side:1");
				if (pcTemp && pcTemp[0])
				{
					// We have the old formatting
					m_pSoundPanelVarBlock->Clear();

					for(size_t i = 0; i < ((mv_height>0.0f)?6:4); ++i)
					{
						CVariable<bool>* const pvTempOld = new CVariable<bool>;

						stack_string cTempOld;
						cTempOld.Format("Side:%d", i+1);

						// And add each to the block
						m_pSoundPanelVarBlock->AddVariable(pvTempOld, cTempOld);
					}
				}

				m_pSoundPanelVarBlock->Serialize(pSoundDataNode, true);
			}

			// Copy the data to the "bools" list
			// First clear the remains out
			m_abObstructSound.clear();

			unsigned int const nVarCount = m_pSoundPanelVarBlock->GetVarsCount();
			for(unsigned int i = 0; i < nVarCount; ++i)
			{
				IVariable const* const pTemp = m_pSoundPanelVarBlock->GetVariable(i);
				if(pTemp)
				{
					bool bTemp = false;
					pTemp->Get(bTemp);
					m_abObstructSound.push_back(bTemp);

					if(i >= nVarCount-2)
					{
						// Here check for backwards compatibility reasons if "ObstructRoof" and "ObstructFloor" still exists
						bool bTemp = false;
						if(i == nVarCount-2)
						{
							if(xmlNode->getAttr("ObstructRoof", bTemp))
								m_abObstructSound[i] = bTemp;
						}
						else
						{
							if(xmlNode->getAttr("ObstructFloor", bTemp))
								m_abObstructSound[i] = bTemp;
						}
					}
				}
			}

			// Since the var block is a static object clear it for the next object to be empty
			m_pSoundPanelVarBlock->Clear();
		}
		else
		{
			XmlNodeRef pSoundDataNode = xmlNode->newChild("SoundData");
			if(pSoundDataNode)
			{
				// TODO: What if mv_height or/and mv_width or/and mv_length == 0 ??
				// For now just serialize all data
				// First clear the remains out
				m_pSoundPanelVarBlock->Clear();

				// Create the variables
				assert(m_abObstructSound.size() == 6);

				size_t nIndex = 0;
				tSoundObstructionIterConst const ItEnd = m_abObstructSound.end();
				for (tSoundObstructionIterConst It = m_abObstructSound.begin(); It != ItEnd ; ++It)
				{
					bool const bObstructed	= (bool)(*It);

					CVariable<bool>* const pvTemp = new CVariable<bool>;
					pvTemp->Set(bObstructed);
					stack_string cTemp;
					cTemp.Format("Side%d", ++nIndex);

					// And add each to the block
					m_pSoundPanelVarBlock->AddVariable(pvTemp, cTemp);
				}

				m_pSoundPanelVarBlock->Serialize(pSoundDataNode, false);
				m_pSoundPanelVarBlock->Clear();
			}
		}
	}
}

//////////////////////////////////////////////////////////////////////////
XmlNodeRef CAreaBox::Export( const CString &levelPath,XmlNodeRef &xmlNode )
{
	XmlNodeRef objNode = CEntityObject::Export( levelPath,xmlNode );
	return objNode;
}

//////////////////////////////////////////////////////////////////////////
void CAreaBox::SetAreaId(int nAreaId)
{
	m_areaId=nAreaId;
	UpdateGameArea();
}

//////////////////////////////////////////////////////////////////////////
int CAreaBox::GetAreaId()
{
	return m_areaId;
}

//////////////////////////////////////////////////////////////////////////
void CAreaBox::SetBox(AABB box)
{
	m_box=box;
	UpdateGameArea();
}

//////////////////////////////////////////////////////////////////////////
AABB CAreaBox::GetBox()
{
	return m_box;
}

//////////////////////////////////////////////////////////////////////////
void CAreaBox::PostLoad( CObjectArchive &ar )
{
	__super::PostLoad(ar );
	// After loading Update game structure.
	UpdateGameArea();
}

//////////////////////////////////////////////////////////////////////////
bool CAreaBox::CreateGameObject()
{
	bool bRes = CEntityObject::CreateGameObject();
	if (bRes)
	{
		UpdateGameArea();
	}
	return bRes;
}

//////////////////////////////////////////////////////////////////////////
void CAreaBox::UpdateGameArea()
{
	if (m_bIgnoreGameUpdate == 0 && m_entity)
	{
		IEntityAreaProxy* const pArea = (IEntityAreaProxy*)m_entity->CreateProxy( ENTITY_PROXY_AREA );
		if (!pArea)
			return;

		bool abObstructSound[6];

		size_t nIndex = 0;
		tSoundObstructionIterConst const ItEnd = m_abObstructSound.end();
		for (tSoundObstructionIterConst It = m_abObstructSound.begin(); It != ItEnd ; ++It)
		{
			// Here we "unpack" the data! (1 bit*nPointsCount to 1 byte*nPointsCount)
			bool const bObstructed	= (bool)(*It);
			abObstructSound[nIndex]	= bObstructed;
			++nIndex;
		}

		UpdateSoundPanelParams();
		pArea->SetBox( m_box.min, m_box.max, &abObstructSound[0], 6 );
		pArea->SetProximity( m_edgeWidth );
		pArea->SetID( m_areaId );
		pArea->SetGroup( mv_groupId );
		pArea->SetPriority( mv_priority );
		pArea->ClearEntities();

		for (int i = 0; i < GetEntityCount(); i++)
		{
			CEntityObject *pEntity = GetEntity(i);
			if (pEntity && pEntity->GetIEntity())
			{
				if(gEnv->pEntitySystem->EntitiesUseGUIDs())
					pArea->AddEntity( pEntity->GetIEntity()->GetGuid() );
				else
					pArea->AddEntity( pEntity->GetIEntity()->GetId() );
			}
		}
	}
}

//////////////////////////////////////////////////////////////////////////
Vec3 CAreaBox::GetLinkAnchor( CBaseObject* pLinkObj )
{
	Vec3 res;

	const Matrix34 &wtm = GetWorldTM();
	Vec3 pos = wtm.GetInverted().TransformPoint( pLinkObj->GetWorldPos() );

	AABB box;
	GetLocalBounds( box );

	Vec3 pts[5];
	Vec3 s = box.GetSize();
	pts[0] = box.min;
	pts[1] = box.min + Vec3( 0,   s.y, 0 );
	pts[2] = box.min + Vec3( s.x, s.y, 0 );
	pts[3] = box.min + Vec3( s.x, 0,   0 );
	pts[4] = pts[0];

	Vec3 intPnt;
	float minDist = FLT_MAX;
	for (int i = 0; i < 4; i++)
	{
		float d = PointToLineDistance( pts[i], pts[i+1], pos, intPnt );
		if (d < minDist)
		{
			minDist = d;
			res = intPnt;
		}
	}
	return wtm.TransformPoint(res);
}


//////////////////////////////////////////////////////////////////////////
void CAreaBox::OnSoundParamsChange( IVariable *var )
{
	if (m_bIgnoreGameUpdate == 0)
	{
		if (!mv_displaySoundInfo)
		{
			if (m_nSoundPanelID != 0)
			{
				// Internally a var block holds "IVariablePtr", on destroy delete is already called
				m_pSoundPanelVarBlock->Clear();

				GetIEditor()->RemoveRollUpPage(ROLLUP_OBJECTS, m_nSoundPanelID);
				m_nSoundPanelID					= 0;
				m_pSoundPropertiesPanel = 0;
			}
		}
		else
		{
			if (!m_pSoundPropertiesPanel)
			{
				m_pSoundPropertiesPanel	= new CPropertiesPanel(AfxGetMainWnd());
			}
		}

		UpdateSoundPanelParams();
	}
}

//////////////////////////////////////////////////////////////////////////
void CAreaBox::OnPointChange( IVariable *var )
{
	if (m_pSoundPanelVarBlock)
	{
		assert(m_abObstructSound.size() == 6);
		unsigned int const nVarCount = m_pSoundPanelVarBlock->GetVarsCount();
		for (unsigned int i = 0; i < nVarCount; ++i)
		{
			if (m_pSoundPanelVarBlock->GetVariable(i) == var)
			{
				if (m_entity)
				{
					IEntityAreaProxy* const pArea = (IEntityAreaProxy*)m_entity->CreateProxy( ENTITY_PROXY_AREA );
					if (pArea)
					{
						bool bValue = false;
						var->Get(bValue);
						pArea->SetSoundObstructionOnAreaFace(i, bValue);
						m_abObstructSound[i] = bValue;
					}
				}
				break;
			}
		}
	}
}

//////////////////////////////////////////////////////////////////////////
void CAreaBox::UpdateSoundPanelParams()
{
	if(!m_rollupMultyId && mv_displaySoundInfo && m_pSoundPropertiesPanel)
	{
		if (!m_nSoundPanelID)
			m_nSoundPanelID	= GetIEditor()->AddRollUpPage(ROLLUP_OBJECTS, "Sound Obstruction", m_pSoundPropertiesPanel);

		m_pSoundPropertiesPanel->DeleteVars();

		if (m_nSoundPanelID)
		{
			if (m_pSoundPanelVarBlock)
			{
				if (!m_pSoundPanelVarBlock->IsEmpty())
					m_pSoundPanelVarBlock->Clear();

				if (!m_abObstructSound.empty())
				{
					// If the box hasn't got a height subtract 2 sides
					assert(m_abObstructSound.size() == 6);
					size_t const nVarCount = m_abObstructSound.size()-((mv_height>0.0f)?0:2);
					for (size_t i = 0; i < nVarCount; ++i)
					{
						CVariable<bool>* const pvTemp = new CVariable<bool>;
						pvTemp->Set(m_abObstructSound[i]);
						pvTemp->AddOnSetCallback(functor(*this, &CAreaBox::OnPointChange));

						stack_string cTemp;
						cTemp.Format("Side:%d", i+1);

						m_pSoundPanelVarBlock->AddVariable(pvTemp, cTemp);
					}
				}

				m_pSoundPropertiesPanel->AddVars(m_pSoundPanelVarBlock);
			}
		}
	}
}